/*
* Copyright 2010 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/

package org.etherframework.behaviour.rules.context
{
	import flash.events.EventDispatcher;
	
	import mx.core.EventPriority;
	
	import org.etherframework.core.ether_internal;
	import org.etherframework.util.CallLater;
	
	use namespace ether_internal;
	
	public class ContextDistributor extends EventDispatcher implements IContextDistributor
	{
		private var callLater:CallLater;
		
		private var pendingFlag:Boolean = false;
		
		private var context:IContext;
		
		private var contextual:IContextual;
		
		private var items:Array;
		
		private var eventPriority:int;
		
		private var contextualSortFunction:Function;
		
		public function ContextDistributor()
		{
			super();
			eventPriority = EventPriority.CURSOR_MANAGEMENT;
			callLater = new CallLater();
		}
		
		private var _idle:Boolean = true;
		
		public function get idle():Boolean
		{
			return _idle;
		}
		
		public function start(contextual:IContextual, context:IContext, contextualSortFunction:Function = null):void
		{
			if(!(contextual is IContextualList))
				return;
			
			this.contextualSortFunction = contextualSortFunction;
			
			if (_idle)
			{
				this.context = context;
				this.contextual = contextual;
				
				_idle = false;
				
				items = IContextualList(contextual).children.concat();
				context.addEventListener(ContextEvent.OPEN, contextOpenHandler, false, eventPriority);
				contextual.context = context;
			}
		}
		
		public function clean():void
		{
			context.removeEventListener(ContextEvent.OPEN, contextOpenHandler);
			context.removeEventListener(ContextEvent.CLOSE, contextCloseHandler);
			context = null;
			contextual = null;
			items = null;
			pendingFlag = false;
		}
		
		public function newInstance():*
		{
			return new ContextDistributor();
		}
		
		private function contextOpenHandler(event:ContextEvent):void
		{
			IContext(event.target).removeEventListener(ContextEvent.OPEN, contextOpenHandler);
			
			next();
		}
		
		private function contextCloseHandler(event:ContextEvent):void
		{
			var closedContext:IContext = event.target as IContext;
			closedContext.removeEventListener(ContextEvent.OPEN, contextOpenHandler);
			closedContext.removeEventListener(ContextEvent.CLOSE, contextCloseHandler);
			
			context.children.push(closedContext);
			context.lastChild = closedContext;
			
			next();
		}
		
		private function commitNext():void
		{
			pendingFlag = false;
			
			if (items.length)
			{
				if (context.closed)
				{
					// If context was closed before its commitNext executed
					// then we get here, so just clean and exit.
					clean();
					_idle = true;
					return;
				}
				
				if (contextualSortFunction is Function)
				{
					items.sort(contextualSortFunction);
				}
				
				var newContextual:IContextual = items.shift() as IContextual;
				var newContext:IContext = context.newInstance(newContextual, context, context.source.initiator, 
															  context.source.reason.kind, context.source.reason.value);
				var distributor:IContextDistributor = newInstance() as IContextDistributor;
				
				newContext.addEventListener(ContextEvent.CLOSE, contextCloseHandler, false, eventPriority);
				context.currentChild = newContext;
				
				if (newContextual is IContextualList)
				{
					distributor.start(newContextual, newContext, contextualSortFunction);
				}
				else
				{
					newContextual.context = newContext;
				}
			}
			else
			{
				complete();
			}
		}
		
		protected function next():void
		{
			if (pendingFlag)
				return;
			
			pendingFlag = true;
			callLater.execute(commitNext);
		}
		
		private function complete():void
		{
			var c:IContext = context;
			c.close();
			clean();
			_idle = true;
		}
		
	}
}